package com.only4play.usercenter.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.only4play.usercenter.utils.RequestSignatureProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author liyuncong
 * @version 1.0
 * @file GatewayRequestFilter
 * @brief GatewayRequestFilter
 * @details GatewayRequestFilter
 * @date 2024-02-01
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2024-02-01               liyuncong          Created
 */
@Slf4j
@Component
public class GatewayRequestFilter implements GlobalFilter {

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RequestSignatureProvider signatureProvider;

    /**
     * 接口签名+接口请求参数加密(请求参数加密可使用https)
     *
     * 1、前端通过非对称算法按照约定好的格式对请求参数和请求体进行加密；
     * 2、前端根据加密好的参数以及时间戳、随机值按照约定好的格式进行方法签名；
     * 3、后端根据方法签名进行校验，校验失败则返回错误信息；
     * 4、后端通过非对称算法按照约定好的格式对加密参数进行解密，将解密数据放回到请求里，重写http请求；
     * 5、后端处理合法请求；
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求头参数
        Map<String, Object> requestHeadersMap = new HashMap<>(16);
        Map<String, List<String>> originHeaderMap = new HashMap<>(exchange.getRequest().getHeaders());
        originHeaderMap.forEach((k, v) -> requestHeadersMap.put(k, v.stream().findFirst().orElse(null)));
        log.info("RequestHeaderParams:{}", requestHeadersMap);

        // 获取请求参数
        Map<String, Object> requestParamsMap = new HashMap<>(16);
        Map<String, List<String>> originQueryMap = new HashMap<>(exchange.getRequest().getQueryParams());
        originQueryMap.forEach((k, v) -> requestParamsMap.put(k, v.stream().findFirst().orElse(null)));
        log.info("RequestQueryParams:{}", requestParamsMap);

        // 获取请求体参数
        Map<String, Object> requestBodyMap = new HashMap<>(16);
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
            log.info("RequestBodyParams: {}", body);
            try {
                Map<String, Object> bodyMap = objectMapper.readValue(body, new TypeReference<>() {
                });
                bodyMap.forEach((k, v) -> requestBodyMap.put(k, String.valueOf(v)));
            } catch (JsonProcessingException e) {
                log.info("获取请求体异常");
            }
            // 接口验证签名
            checkRepeatAndSignature(requestHeadersMap, requestParamsMap, requestBodyMap);
            return Mono.just(body);
        });
        //创建BodyInserter修改请求体
        return decorator(exchange, chain, modifiedBody);
    }

    /**
     * 修改请求体
     */
    private Mono<Void> decorator(ServerWebExchange exchange, GatewayFilterChain chain, Mono<String> modifiedBody) {
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        headers.remove(HttpHeaders.CONTENT_LENGTH);
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
            ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return outputMessage.getBody();
                }
            };
            return chain.filter(exchange.mutate().request(decorator).build());
        }));
    }

    /**
     * 签名防重校验
     *
     * @param requestHeadersMap requestHeadersMap
     * @param requestParamsMap  requestParamsMap
     * @param requestBodyMap    requestBodyMap
     */
    public void checkRepeatAndSignature(
        Map<String, Object> requestHeadersMap, Map<String, Object> requestParamsMap, Map<String, Object> requestBodyMap
    ) {
        signatureProvider.validate(requestHeadersMap, requestParamsMap, requestBodyMap);
    }

}
